﻿/*=====================================================================
 * Arquivo:	Connection.cs
 * 
 * Sumário: Definição da classe base que controla a conexão com o banco de 
 *					dados da aplicação.
 * 
 * Autor:		Siddhartha Argollo
 * 
 * Copyright (C) TecnoTRENDS.  Todos os direitos reservados.
 =====================================================================*/
using System;
using System.Data;
using System.Data.SqlClient;
using System.Collections;
using System.Collections.Generic;
using System.Data.Odbc;
using System.Data.OleDb;
using DaCredito;

namespace Trends.TFC.Data
{
    /// <summary>
    /// Controla a conexão com o banco de dados da aplicação e implementa a 
    /// interface padrão de conexão, IDbConnection.
    /// </summary>
    /// <remarks>
    /// Esta classe foi implementada de modo a encapsular o objeto 
    /// AdoTransaction de uma DataWindow.
    /// </remarks>
    public class Connection : IDbConnection
    {
        // Armazena a lista de DBMS disponíveis
        private static Hashtable soDBMSList = null;

        // Armazena as propriedades da conexão
        private PropertyList ioProperties = new PropertyList();
        // Armazena a conexão com o banco
        private IDbConnection ioConnection;
        // Armazena o DBMS sendo usado na conexão
        private DBMSInfo ioDBMS;
        // Transação usada pela conexão
        private IDbTransaction ioDbTransaction;
        private int iiConnected = 0;

        /// <summary>
        /// Evento chamado quando o estado da conexão é alterado.
        /// </summary>
        public event EventHandler StateChange;

        /// <summary>
        /// Instancia o objeto Connection para uma conexão IDbConnection.
        /// </summary>
        /// <param name="aoConnection"></param>
        public Connection(IDbConnection aoConnection)
            : this(aoConnection.GetType())
        {
            this.ConnectionString = aoConnection.ConnectionString;
        }

        /// <summary>
        /// Instancia o objeto Connection para o tipo de conexão requisitado.
        /// </summary>
        /// <param name="aoConnection"></param>
        public Connection(Type aoConnectionType)
        {
            this.ioDBMS = GetDBMSInfo(aoConnectionType);
            if (this.ioDBMS == null)
                throw new Exception("teste");
            this.ioConnection = (IDbConnection)Activator.CreateInstance(aoConnectionType);
        }

        /// <summary>
        /// Instancia o objeto para o DBMS requisitado.
        /// </summary>
        /// <param name="asDBMSName">Nome do DBMS.</param>
        public Connection(string asDBMSName)
            : this(GetDBMSInfo(asDBMSName))
        {
        }

        /// <summary>
        /// Instancia o objeto para o DBMS requisitado.
        /// </summary>
        /// <param name="aoDBMS">Objeto DBMSInfo.</param>
        public Connection(DBMSInfo aoDBMS)
        {
            this.ioDBMS = aoDBMS;
            if (this.ioDBMS == null)
                throw new Exception("teste");
            this.ioConnection = aoDBMS.CreateConnection();
        }

        #region Static methods

        /// <summary>
        /// Constrói uma lista de conexões padrões disponíveis para a realização
        /// de conexões.
        /// </summary>
        static Connection()
        {
            DBMSInfo loDBMS;
            // TODO: Carregar essas informações de um arquivo xml.
            ArrayList loList = new ArrayList();

            // ODBC
            loList.Add("Pwd");
            loList.Add("Uid");
            loList.Add("DSN");
            loList.Add("Driver");
            loList.Add("Server");
            loList.Add("Connection");
            loDBMS = new DBMSInfo("ODBC", typeof(OdbcConnection), typeof(OdbcParameter),
              false, "?", loList);
            loDBMS.HibernateProperties.Add("hibernate.dialect",
              "NHibernate.Dialect.MsSql2000Dialect");
            loDBMS.HibernateProperties.Add("hibernate.connection.driver_class",
              "NHibernate.Driver.OdbcDriver");
            RegisterDBMS(loDBMS);

            // OleDb
            loList = new ArrayList();
            loList.Add("Password");
            loList.Add("User Id");
            loList.Add("Provider");
            loList.Add("Data Source");
            loList.Add("Connection");
            loList.Add("Initial Catalog");
            loList.Add("Database");
            loDBMS = new DBMSInfo("OleDb", typeof(OleDbConnection), typeof(OleDbParameter),
              false, "?", loList);
            loDBMS.HibernateProperties.Add("hibernate.dialect",
              "NHibernate.Dialect.MsSql2000Dialect");
            loDBMS.HibernateProperties.Add("hibernate.connection.driver_class",
              "NHibernate.Driver.OleDbDriver");
            RegisterDBMS(loDBMS);

            // SQL Server
            loList = new ArrayList();
            loList.Add("Password");
            loList.Add("User Id");
            loList.Add("Database");
            loList.Add("Network Library");
            loList.Add("Data Source");
            loList.Add("Initial Catalog");
            loList.Add("Application Name");
            loList.Add("Connection Timeout=15");
            loList.Add("Current Language");
            loList.Add("Trusted_Connection=no");
            loList.Add("Encrypt=false");
            loList.Add("Integrated Security=false");
            loList.Add("Persist Security Info=false");
            loList.Add("Max Pool Size");
            loDBMS = new DBMSInfo("SQL Server", typeof(SqlConnection), typeof(SqlParameter),
              true, "@", loList);
            loDBMS.HibernateProperties.Add("hibernate.dialect",
              "NHibernate.Dialect.MsSql2000Dialect");
            loDBMS.HibernateProperties.Add("hibernate.connection.driver_class",
              "NHibernate.Driver.SqlClientDriver");
            RegisterDBMS(loDBMS);

            /*
                  loList = new ArrayList();
                  loList.Add("Password");
                  loList.Add("User Id");
                  loList.Add("Data Source");
                  loList.Add("Integrated Security=false");
                  loList.Add("Persist Security Info=false");
                  loList.Add("Unicode=false");
                  loList.Add("Connection Lifetime=0");
                  loList.Add("Enlist=true");
                  loList.Add("Max Pool Size=0");
                  loList.Add("Pooling=true");
                  RegisterDBMS(new DBMSInfo("Oracle", typeof(OracleConnection), 
                    typeof(OracleParameter), true, ":", loList));
            */
        }

        /// <summary>
        /// Retorna o objeto DBMSInfo para um determinado DBMS.
        /// </summary>
        /// <param name="asName">Nome do DBMS.</param>
        /// <returns>O objeto DBMSInfo para um determinado DBMS.</returns>
        public static DBMSInfo GetDBMSInfo(string asName)
        {
            if (soDBMSList.ContainsKey(asName))
                return (DBMSInfo)soDBMSList[asName];
            return null;
        }

        /// <summary>
        /// Retorna o objeto DBMSInfo referente ao objeto IDbConnection.
        /// </summary>
        /// <param name="aoConnection">
        /// Um objeto IDbConnection.
        /// </param>
        /// <returns>
        /// O objeto DBMSInfo referente ao objeto IDbConnection.
        /// </returns>
        public static DBMSInfo GetDBMSInfo(Type aoConnectionType)
        {
            foreach (DictionaryEntry entry in soDBMSList)
                if (((DBMSInfo)entry.Value).ConnectionType.Equals(aoConnectionType))
                    return (DBMSInfo)entry.Value;
            return null;
        }

        /// <summary>
        /// Retorna a lista de objetos DBMSInfo registrados.
        /// </summary>
        /// <param name="aoList"></param>
        public static void GetDBMSList(IList aoList)
        {
            foreach (DBMSInfo info in soDBMSList.Values)
                aoList.Add(info);
        }

        /// <summary>
        /// Registra as informações de conexão a um determinado DBMS.
        /// </summary>
        /// <param name="ioInfo">Informações de conexão com o DBMS.</param>
        public static void RegisterDBMS(DBMSInfo ioInfo)
        {
            if (soDBMSList == null)
                soDBMSList = new Hashtable();
            soDBMSList.Add(ioInfo.Name, ioInfo);
        }

        #endregion

        #region Public Members

        /// <summary>
        /// Objeto de transação associado com a conexão.
        /// </summary>
        public virtual object Transaction
        {
            get { return this.DbTransaction; }
        }

        /// <summary>
        /// Valor de uma determinada propriedade da conexão.
        /// </summary>
        public object this[string asKey]
        {
            get { return this.ioProperties[asKey]; }
            set { this.ioProperties[asKey] = value; }
        }

        /// <summary>
        /// Propriedades da conexão.
        /// </summary>
        public PropertyList Properties
        {
            get { return this.ioProperties; }
        }

        /// <summary>
        /// Retorna o estado da conexão.
        /// </summary>
        public virtual ConnectionState State
        {
            get { return this.ioConnection.State; }
        }

        /// <summary>
        /// Verifica se existe uma transação aberta.
        /// </summary>
        /// <returns>True se existe uma transação aberta</returns>
        public virtual bool IsInTransaction
        {
            get { return (this.DbTransaction != null); }
        }

        /// <summary>
        /// DBMS ao qual a conexão será realizada. 
        /// </summary>
        public DBMSInfo DBMS
        {
            get { return this.ioDBMS; }
            set
            {
                this.ForceClosed();
                this.ioDBMS = value;
                this.ioConnection = this.ioDBMS.CreateConnection();
            }
        }

        /// <summary>
        /// Retorna o objeto IDbConnection nativo.
        /// </summary>
        public IDbConnection DbConnection
        {
            get { return this.ioConnection; }
        }

        /// <summary>
        /// Indica se a conexão está ativa ou não.
        /// </summary>
        public virtual bool IsActive
        {
            get { return (this.ioConnection != null && this.State != ConnectionState.Closed); }
        }

        /// <summary>
        /// Retorna a representação em string do objeto.
        /// </summary>
        /// <returns>A representação em string do objeto.</returns>
        public override string ToString()
        {
            return string.Format("{0};{1};{2}", this.ioDBMS, this.IsActive,
              this.ConnectionString);
        }

        /// <summary>
        /// Abre a conexão com o DBMS.
        /// </summary>
        public virtual void Open()
        {
            if (this.iiConnected == 0)
            {
                Logger.Log(this);
                this.ioConnection.ConnectionString = this.ioProperties.ToString(this.ioDBMS.Separator);
                this.ioConnection.Open();
                try
                {
                    this.OnOpen();
                }
                catch (Exception ex)
                {
                    this.ioConnection.Close();
                    throw ex;
                }
                this.OnStateChange();
            }
            this.iiConnected++;
        }

        /// <summary>
        /// Termina a conexão com o DBMS.
        /// </summary>
        public virtual void Close()
        {
            if (this.iiConnected > 0)
                this.iiConnected--;
            if (this.iiConnected == 0)
            {
                this.OnClose();
                this.ioConnection.Close();
               //teste Logger.Log(this).Debug("Connection closed.");
                this.OnStateChange();
            }
        }

        public void ForceClose()
        {
            this.iiConnected = 0;
            this.Close();
        }

        /// <summary>
        /// Indica se a conexão com o objeto WinForms remoto está ativa.
        /// </summary>
        public virtual bool IsRemoteConnected
        {
            get { return false; }
        }

        /// <summary>
        /// Realiza a conexão com o objeto WinForms remoto.
        /// </summary>
        public virtual void RemoteConnect()
        {
            /* Nothing to do */
        }

        /// <summary>
        /// Realiza a desconexão com o objeto WinForms remoto.
        /// </summary>
        public virtual void RemoteDisconnect()
        {
            /* Nothing to do */
        }

        /// <summary>
        /// Inicia uma transação.
        /// </summary>
        /// <returns>True se a transação for aberta com sucesso, false
        /// caso contrário.</returns>
        public bool BeginTransaction()
        {
            return this.BeginTransaction(IsolationLevel.Unspecified);
        }

        /// <summary>
        /// Inicia uma transação.
        /// </summary>
        /// <param name="aeLevel">
        /// Especifica o comportamento de Lock da transação.
        /// </param>
        /// <returns>True se a transação for aberta com sucesso, false
        /// caso contrário.</returns>
        public virtual bool BeginTransaction(IsolationLevel aeLevel)
        {
            if (aeLevel == IsolationLevel.Unspecified)
                this.DbTransaction = this.ioConnection.BeginTransaction();
            else
                this.DbTransaction = this.ioConnection.BeginTransaction(aeLevel);
            return (this.DbTransaction != null);
        }

        /// <summary>
        /// Encerra uma transação aberta, salvando as alterações realizadas no banco.
        /// </summary>
        public virtual void Commit()
        {
            this.ForceTrans();
            this.DbTransaction.Commit();
            this.DbTransaction = null;
        }

        /// <summary>
        /// Encerra uma transação aberta, descartando as alterações realizadas no banco.
        /// </summary>
        public virtual void Rollback()
        {
            this.ForceTrans();
            this.DbTransaction.Rollback();
            this.DbTransaction = null;
        }

        /// <summary>
        /// Cria uma array de objetos IDataParameter com os valores recebidos.
        /// </summary>
        /// <param name="aoParams">Valores que serão usados na criação do array;
        /// deve conter um número par da elementos, onde o primeiro será o nome do
        /// parâmetro e o segundo, o seu valor.
        /// </param>
        /// <returns></returns>
        public IDataParameter[] CreateParameters(params object[] aoParams)
        {
            if (aoParams.Length % 2 != 0)
                throw new Exception("teste");
            IDataParameter[] loData = new IDataParameter[aoParams.Length / 2];
            int liCount = 0;
            for (int i = 0; i < loData.Length; i++)
            {
                loData[i] = this.ioDBMS.CreateParameter(aoParams[liCount].ToString(),
                  aoParams[liCount + 1]);
                liCount += 2;
            }
            return loData;
        }

        /// <summary>
        /// Cria um objeto IDataParameter a partir do nome e seu valor.
        /// </summary>
        /// <param name="asName">Nome do parâmetro.</param>
        /// <param name="aoValue">Valor  do parâmetro.</param>
        /// <returns>Objeto IDataParameter.</returns>
        public IDataParameter CreateParameter(string asName, object aoValue)
        {
            return this.ioDBMS.CreateParameter(asName, aoValue);
        }

        /// <summary>
        /// Executa um comando SQL no banco.
        /// </summary>
        /// <param name="asSql">Comando SQL a ser executado.</param>
        /// <returns>O número de registros atingidos pelo comando.</returns>
        public int ExecSql(string asSql)
        {
            return this.ExecSql(asSql, CommandType.Text, null);
        }

        /// <summary>
        /// Executa um comando SQL parametrizado no banco.
        /// </summary>
        /// <param name="asSql">Comando SQL a ser executado.</param>
        /// <param name="aoParams">Os valores dos parâmetros do comando.</param>
        /// <returns>O número de registros atingidos pelo comando.</returns>
        public int ExecSql(string asSql, params IDataParameter[] aoParams)
        {
            return this.ExecSql(asSql, CommandType.Text, aoParams);
        }

        /// <summary>
        /// Executa um comando SQL parametrizado no banco.
        /// </summary>
        /// <remarks>
        /// <para>Se o tipo de comando for StoredProcedure, o parâmetro asSql
        /// deve conter o nome do procedimento a ser executado.</para>
        /// </remarks>
        /// <param name="asSql">Comando SQL a ser executado.</param>
        /// <param name="aeType"><see cref="System.Data.CommandType"/></param>
        /// <returns>O número de registros atingidos pelo comando.</returns>
        public int ExecSql(string asSql, CommandType aeType)
        {
            return this.ExecSql(asSql, aeType, null);
        }

        /// <summary>
        /// Executa um comando SQL parametrizado no banco.
        /// </summary>
        /// <remarks>
        /// <para>Se o tipo de comando for StoredProcedure, o parâmetro asSql
        /// deve conter o nome do procedimento a ser executado.</para>
        /// </remarks>
        /// <param name="asSql">Comando SQL a ser executado.</param>
        /// <param name="aeType"><see cref="System.Data.CommandType"/></param>
        /// <param name="aoParams">Os valores dos parâmetros do comando.</param>
        /// <returns>O número de registros atingidos pelo comando.</returns>
        public int ExecSql(string asSql, CommandType aeType, params IDataParameter[] aoParams)
        {
            int liResult = -1;
            using (IDbCommand loCommand = this.ioConnection.CreateCommand())
            {
                loCommand.CommandText = this.ioDBMS.ConvertSqlParameters(asSql,
                  ref aoParams);
                loCommand.CommandType = aeType;
                if (this.IsInTransaction)
                    loCommand.Transaction = this.DbTransaction;
                if (aoParams != null)
                    foreach (object param in aoParams)
                        loCommand.Parameters.Add(param);
                liResult = loCommand.ExecuteNonQuery();
                loCommand.Parameters.Clear();
            }
            return liResult;
        }

        /// <summary>
        /// Abre um comando SELECT, retornando os respectivos registros.
        /// </summary>
        /// <param name="asSql">Comando SELECT a ser executado.</param>
        /// <returns>Objeto IDataReader contendo os registros retornados.</returns>
        public IDataReader OpenSql(string asSql)
        {
            return this.OpenSql(asSql, CommandType.Text, null);
        }

        /// <summary>
        /// Abre um comando SELECT parametrizado, retornando os respectivos 
        /// registros.
        /// </summary>
        /// <remarks>
        /// <para>Pode-se usar o seguinte formato genérico para indicar os 
        /// parâmetros do sql: {nome_parametro}. Todos os parâmetros encontrados
        /// nesse formato serão convertidos para o formato suportado pelo DBMS da
        /// conexão.</para>
        /// </remarks>
        /// <param name="asSql">Comando SELECT a ser executado.</param>
        /// <param name="aoParams">Os valores dos parâmetros do comando.</param>
        /// <returns>Objeto IDataReader contendo os registros retornados.</returns>
        public IDataReader OpenSql(string asSql, params IDataParameter[] aoParams)
        {
            return this.OpenSql(asSql, CommandType.Text, aoParams);
        }

        /// <summary>
        /// Abre um comando SELECT parametrizado, retornando os respectivos 
        /// registros.
        /// </summary>
        /// <remarks>
        /// <para>Pode-se usar o seguinte formato genérico para indicar os 
        /// parâmetros do sql: {nome_parametro}. Todos os parâmetros encontrados
        /// nesse formato serão convertidos para o formato suportado pelo DBMS da
        /// conexão.</para>
        /// <para>Se o tipo de comando for StoredProcedure, o parâmetro asSql
        /// deve conter o nome do procedimento a ser executado e, caso seja
        /// TableDirect, o nome da tabela a ser aberta.</para>
        /// </remarks>
        /// <param name="asSql">Comando SELECT a ser executado.</param>
        /// <param name="aeType"><see cref="System.Data.CommandType"/></param>
        /// <returns>Objeto IDataReader contendo os registros retornados.</returns>
        public IDataReader OpenSql(string asSql, CommandType aeType)
        {
            return this.OpenSql(asSql, aeType, null);
        }

        /// <summary>
        /// Abre um comando SELECT parametrizado, retornando os respectivos 
        /// registros.
        /// </summary>
        /// <remarks>
        /// <para>Pode-se usar o seguinte formato genérico para indicar os 
        /// parâmetros do sql: {nome_parametro}. Todos os parâmetros encontrados
        /// nesse formato serão convertidos para o formato suportado pelo DBMS da
        /// conexão.</para>
        /// <para>Se o tipo de comando for StoredProcedure, o parâmetro asSql
        /// deve conter o nome do procedimento a ser executado e, caso seja
        /// TableDirect, o nome da tabela a ser aberta.</para>
        /// </remarks>
        /// <param name="asSql">Comando SELECT a ser executado.</param>
        /// <param name="aeType"><see cref="System.Data.CommandType"/></param>
        /// <param name="aoParams">Os valores dos parâmetros do comando.</param>
        /// <returns>Objeto IDataReader contendo os registros retornados.</returns>
        public IDataReader OpenSql(string asSql, CommandType aeType, params IDataParameter[] aoParams)
        {
            IDbCommand loCommand = this.ioConnection.CreateCommand();
            loCommand.CommandType = aeType;
            if (aeType == CommandType.StoredProcedure)
                loCommand.CommandText = asSql;
            else
                loCommand.CommandText = this.ioDBMS.ConvertSqlParameters(asSql,
                  ref aoParams);
            if (this.IsInTransaction)
                loCommand.Transaction = this.DbTransaction;
            if (aoParams != null)
                foreach (object param in aoParams)
                    loCommand.Parameters.Add(param);
            IDataReader loReader = new DataReader(loCommand);
            loCommand.Parameters.Clear();
            return loReader;
        }

        /// <summary>
        /// Abre um comando SELECT parametrizado, retornando os respectivos 
        /// registros em um array de objetos.
        /// </summary>
        /// <remarks>
        /// <para>Pode-se usar o seguinte formato genérico para indicar os 
        /// parâmetros do sql: {nome_parametro}. Todos os parâmetros encontrados
        /// nesse formato serão convertidos para o formato suportado pelo DBMS da
        /// conexão.</para>
        /// <para>Se o tipo de comando for StoredProcedure, o parâmetro asSql
        /// deve conter o nome do procedimento a ser executado e, caso seja
        /// TableDirect, o nome da tabela a ser aberta.</para>
        /// </remarks>
        /// <param name="asSql">Comando SELECT a ser executado.</param>
        /// <returns>
        /// Array de objetos, contendo os resultados da consulta.
        /// </returns>
        public List<object[]> LoadSql(string asSql)
        {
            return this.LoadSql(asSql, CommandType.Text, null);
        }

        /// <summary>
        /// Abre um comando SELECT parametrizado, retornando os respectivos 
        /// registros em um array de objetos.
        /// </summary>
        /// <remarks>
        /// <para>Pode-se usar o seguinte formato genérico para indicar os 
        /// parâmetros do sql: {nome_parametro}. Todos os parâmetros encontrados
        /// nesse formato serão convertidos para o formato suportado pelo DBMS da
        /// conexão.</para>
        /// <para>Se o tipo de comando for StoredProcedure, o parâmetro asSql
        /// deve conter o nome do procedimento a ser executado e, caso seja
        /// TableDirect, o nome da tabela a ser aberta.</para>
        /// </remarks>
        /// <param name="asSql">Comando SELECT a ser executado.</param>
        /// <param name="aeType"><see cref="System.Data.CommandType"/></param>
        /// <returns>
        /// Array de objetos, contendo os resultados da consulta.
        /// </returns>
        public List<object[]> LoadSql(string asSql, CommandType aeType)
        {
            return this.LoadSql(asSql, aeType, null);
        }

        /// <summary>
        /// Abre um comando SELECT parametrizado, retornando os respectivos 
        /// registros em um array de objetos.
        /// </summary>
        /// <remarks>
        /// <para>Pode-se usar o seguinte formato genérico para indicar os 
        /// parâmetros do sql: {nome_parametro}. Todos os parâmetros encontrados
        /// nesse formato serão convertidos para o formato suportado pelo DBMS da
        /// conexão.</para>
        /// <para>Se o tipo de comando for StoredProcedure, o parâmetro asSql
        /// deve conter o nome do procedimento a ser executado e, caso seja
        /// TableDirect, o nome da tabela a ser aberta.</para>
        /// </remarks>
        /// <param name="asSql">Comando SELECT a ser executado.</param>
        /// <param name="aoParams">Os valores dos parâmetros do comando.</param>
        /// <returns>
        /// Array de objetos, contendo os resultados da consulta.
        /// </returns>
        public List<object[]> LoadSql(string asSql, params IDataParameter[] aoParams)
        {
            return this.LoadSql(asSql, CommandType.Text, aoParams);
        }

        /// <summary>
        /// Abre um comando SELECT parametrizado, retornando os respectivos 
        /// registros em um array de objetos.
        /// </summary>
        /// <remarks>
        /// <para>Pode-se usar o seguinte formato genérico para indicar os 
        /// parâmetros do sql: {nome_parametro}. Todos os parâmetros encontrados
        /// nesse formato serão convertidos para o formato suportado pelo DBMS da
        /// conexão.</para>
        /// <para>Se o tipo de comando for StoredProcedure, o parâmetro asSql
        /// deve conter o nome do procedimento a ser executado e, caso seja
        /// TableDirect, o nome da tabela a ser aberta.</para>
        /// </remarks>
        /// <param name="asSql">Comando SELECT a ser executado.</param>
        /// <param name="aeType"><see cref="System.Data.CommandType"/></param>
        /// <param name="aoParams">Os valores dos parâmetros do comando.</param>
        /// <returns>
        /// Array de objetos, contendo os resultados da consulta.
        /// </returns>
        public List<object[]> LoadSql(string asSql, CommandType aeType, params IDataParameter[] aoParams)
        {
            List<object[]> loResults = new List<object[]>();
            using (IDataReader loReader = this.OpenSql(asSql, aeType, aoParams))
            {
                while (loReader.Read())
                {
                    object[] loValues = new object[loReader.FieldCount];
                    for (int i = 0; i < loReader.FieldCount; i++)
                        loValues[i] = loReader[i];
                    loResults.Add(loValues);
                }
            }
            return loResults;
        }

        /// <summary>
        /// Utiliza a parte FROM do SQL recebido para executar uma consulta do tipo
        /// SELECT COUNT(*).
        /// </summary>
        /// <param name="asSql">Texto da consulta.</param>
        /// <returns>A quantidade retornada pelo COUNT.</returns>
        public int CountSql(string asSql)
        {
            return this.CountSql(asSql, null);
        }

        /// <summary>
        /// Utiliza a parte FROM do SQL recebido para executar uma consulta do tipo
        /// SELECT COUNT(*).
        /// </summary>
        /// <param name="asSql">Texto da consulta.</param>
        /// <param name="loParams">Os parâmetros da consulta ou null para uma
        /// consulta sem parâmetros.</param>
        /// <returns>A quantidade retornada pelo COUNT.</returns>
        public int CountSql(string asSql, params IDataParameter[] loParams)
        {
            int liPos = asSql.IndexOf("FROM");
            if (liPos < 0)
                throw new Exception("teste");
            int liResult;
            string lsCountSql = "SELECT COUNT(*) " + asSql.Substring(liPos);
            liPos = lsCountSql.IndexOf("ORDER BY");
            if (liPos > 0)
                lsCountSql = lsCountSql.Substring(0, liPos);
            if (this.SelectInto(lsCountSql, loParams, out liResult))
                return liResult;
            return 0;
        }

        /// <summary>
        /// Retorna um array contendo os valores das colunas da primeira linha
        /// retornada pelo comando SELECT, ou null se não existir registros
        /// retornados.
        /// </summary>
        /// <param name="asSql">Comando SELECT a ser executado.</param>
        /// <returns>Um objeto IDictionary contendo os nomes das colunas e seus 
        /// respectivos valores da primeira linha retornada, ou  null se nenhum
        /// registro foi encontrado.</returns>
        public IDictionary<string, object> SelectFirst(string asSql)
        {
            return this.SelectFirst(asSql, null);
        }

        /// <summary>
        /// Retorna um array contendo os valores das colunas da primeira linha
        /// retornada pelo comando SELECT parametrizado, ou null se nenhum registro
        /// foi encontrado.
        /// </summary>
        /// <param name="asSql">Comando SELECT a ser executado.</param>
        /// <param name="aoParams">Os valores dos parâmetros do comando.</param>
        /// <returns>Um objeto IDictionary contendo os nomes das colunas e seus 
        /// respectivos valores da primeira linha retornada, ou  null se nenhum
        /// registro foi encontrado.</returns>
        public IDictionary<string, object> SelectFirst(string asSql, params IDataParameter[] aoParams)
        {
            Dictionary<string, object> loResult = null;
            IDataReader loRows = OpenSql(asSql, aoParams);
            try
            {
                if (loRows.Read())
                {
                    loResult = new Dictionary<string, object>();
                    for (int i = 0; i < loRows.FieldCount; i++)
                        loResult.Add(loRows.GetName(i), loRows.GetValue(i));
                }
            }
            finally
            {
                loRows.Close();
            }
            return loResult;
        }

        /// <summary>
        /// Realiza uma consulta SQL, armazenando o valor de retorno (que deve ser único)
        /// no parâmetro <c>alResult</c>.
        /// </summary>
        /// <param name="asSql">Consulta SQL a ser executada.</param>
        /// <param name="alResult">Valor de retorno da consulta.</param>
        /// <returns>True se a consulta retornar algum valor, false caso contrário.</returns>
        public bool SelectInto(string asSql, out long alResult)
        {
            return this.SelectInto(asSql, null, out alResult);
        }

        /// <summary>
        /// Realiza uma consulta SQL, armazenando o valor de retorno (que deve ser único)
        /// no parâmetro <c>asResult</c>.
        /// </summary>
        /// <param name="asSql">Consulta SQL a ser executada.</param>
        /// <param name="asResult">Valor de retorno da consulta.</param>
        /// <returns>True se a consulta retornar algum valor, false caso contrário.</returns>
        public bool SelectInto(string asSql, out string asResult)
        {
            return this.SelectInto(asSql, null, out asResult);
        }

        /// <summary>
        /// Realiza uma consulta SQL, armazenando o valor de retorno (que deve ser único)
        /// no parâmetro <c>asResult</c>.
        /// </summary>
        /// <param name="asSql">Consulta SQL a ser executada.</param>
        /// <param name="aoParams">Parâmetros a serem usados na execução da consulta.</param>
        /// <param name="asResult">Valor de retorno da consulta.</param>
        /// <returns></returns>
        public bool SelectInto(string asSql, IDataParameter[] aoParams, out string asResult)
        {
            object loResult;
            this.SelectInto(asSql, aoParams, out loResult);
            if (loResult != null && loResult != DBNull.Value)
            {
                asResult = Convert.ToString(loResult).Trim();
                return true;
            }
            asResult = "";
            return false;
        }

        /// <summary>
        /// Realiza uma consulta SQL, armazenando o valor de retorno (que deve ser único)
        /// no parâmetro <c>alResult</c>.
        /// </summary>
        /// <param name="asSql">Consulta SQL a ser executada.</param>
        /// <param name="aoParams">Parâmetros a serem usados na execução da consulta.</param>
        /// <param name="alResult">Valor de retorno da consulta.</param>
        /// <returns></returns>
        public bool SelectInto(string asSql, IDataParameter[] aoParams, out long alResult)
        {
            object loResult;
            this.SelectInto(asSql, aoParams, out loResult);
            if (loResult != DBNull.Value && loResult != null)
            {
                alResult = Convert.ToInt64(loResult);
                return true;
            }
            alResult = 0;
            return false;
        }

        /// <summary>
        /// Realiza uma consulta SQL, armazenando o valor de retorno (que deve ser único)
        /// no parâmetro <c>aiResult</c>.
        /// </summary>
        /// <param name="asSql">Consulta SQL a ser executada.</param>
        /// <param name="aiResult">Valor de retorno da consulta.</param>
        /// <returns>True se a consulta retornar algum valor, false caso contrário.</returns>
        public bool SelectInto(string asSql, out int aiResult)
        {
            return this.SelectInto(asSql, null, out aiResult);
        }

        /// <summary>
        /// Realiza uma consulta SQL, armazenando o valor de retorno (que deve ser único)
        /// no parâmetro <c>aiResult</c>.
        /// </summary>
        /// <param name="asSql">Consulta SQL a ser executada.</param>
        /// <param name="aoParams">Parâmetros a serem usados na execução da consulta.</param>
        /// <param name="aiResult">Valor de retorno da consulta.</param>
        /// <returns></returns>
        public bool SelectInto(string asSql, IDataParameter[] aoParams, out int aiResult)
        {
            object loResult;
            this.SelectInto(asSql, aoParams, out loResult);
            if (loResult != DBNull.Value && loResult != null)
            {
                aiResult = Convert.ToInt32(loResult);
                return true;
            }
            aiResult = 0;
            return false;
        }

        /// <summary>
        /// Realiza uma consulta SQL, armazenando o valor de retorno (que deve ser único)
        /// no parâmetro <c>adResult</c>.
        /// </summary>
        /// <param name="asSql">Consulta SQL a ser executada.</param>
        /// <param name="adResult">Valor de retorno da consulta.</param>
        /// <returns>True se a consulta retornar algum valor, false caso contrário.</returns>
        public bool SelectInto(string asSql, out double adResult)
        {
            return this.SelectInto(asSql, null, out adResult);
        }

        /// <summary>
        /// Realiza uma consulta SQL, armazenando o valor de retorno (que deve ser único)
        /// no parâmetro <c>adResult</c>.
        /// </summary>
        /// <param name="asSql">Consulta SQL a ser executada.</param>
        /// <param name="aoParams">Parâmetros a serem usados na execução da consulta.</param>
        /// <param name="adResult">Valor de retorno da consulta.</param>
        /// <returns></returns>
        public bool SelectInto(string asSql, IDataParameter[] aoParams, out double adResult)
        {
            object loResult;
            this.SelectInto(asSql, aoParams, out loResult);
            if (loResult != DBNull.Value && loResult != null)
            {
                adResult = Convert.ToDouble(loResult);
                return true;
            }
            adResult = 0;
            return false;
        }

        /// <summary>
        /// Realiza uma consulta SQL, armazenando o valor de retorno (que deve ser único)
        /// no parâmetro <c>adcResult</c>.
        /// </summary>
        /// <param name="asSql">Consulta SQL a ser executada.</param>
        /// <param name="adcResult">Valor de retorno da consulta.</param>
        /// <returns>True se a consulta retornar algum valor, false caso contrário.</returns>
        public bool SelectInto(string asSql, out decimal adcResult)
        {
            return this.SelectInto(asSql, null, out adcResult);
        }

        /// <summary>
        /// Realiza uma consulta SQL, armazenando o valor de retorno (que deve ser único)
        /// no parâmetro <c>adcResult</c>.
        /// </summary>
        /// <param name="asSql">Consulta SQL a ser executada.</param>
        /// <param name="aoParams">Parâmetros a serem usados na execução da consulta.</param>
        /// <param name="adcResult">Valor de retorno da consulta.</param>
        /// <returns></returns>
        public bool SelectInto(string asSql, IDataParameter[] aoParams, out decimal adcResult)
        {
            object loResult;
            this.SelectInto(asSql, aoParams, out loResult);
            if (loResult != DBNull.Value && loResult != null)
            {
                adcResult = Convert.ToDecimal(loResult);
                return true;
            }
            adcResult = 0;
            return false;
        }

        /// <summary>
        /// Realiza uma consulta SQL, armazenando o valor de retorno (que deve ser único)
        /// no parâmetro <c>aoResult</c>.
        /// </summary>
        /// <param name="asSql">Consulta SQL a ser executada.</param>
        /// <param name="aoParams">Parâmetros a serem usados na execução da consulta.</param>
        /// <param name="aoResult">Valor de retorno da consulta.</param>
        /// <returns></returns>
        public void SelectInto(string asSql, IDataParameter[] aoParams, out object aoResult)
        {
            IDataReader loRows = OpenSql(asSql, aoParams);
            try
            {
                if (!loRows.Read())
                    throw new Exception("teste");
                aoResult = loRows.GetValue(0);
            }
            finally
            {
                loRows.Close();
            }
        }

        #endregion

        #region Protected Members

        /// <summary>
        /// Objeto interno de transação associado com a conexão.
        /// </summary>
        protected virtual IDbTransaction DbTransaction
        {
            get { return this.ioDbTransaction; }
            set { this.ioDbTransaction = value; }
        }

        protected virtual void OnOpen()
        {
        }

        protected virtual void OnClose()
        {
        }

        /// <summary>
        /// Verifica se existe uma transação aberta. Caso não exista,
        /// levanta uma exceção.
        /// </summary>
        protected void ForceTrans()
        {
            if (this.DbTransaction == null)
                throw new Exception("teste");
        }

        /// <summary>
        /// Levanta o evento StateChange.
        /// </summary>
        protected virtual void OnStateChange()
        {
            if (this.StateChange != null)
                this.StateChange(this, EventArgs.Empty);
        }

        /// <summary>
        /// Verifica se a conexão está fechada. Caso não esteja, levanta uma exceção.
        /// </summary>
        protected void ForceClosed()
        {
            if (this.IsActive)
                throw new Exception("teste");
        }

        #endregion

        #region IDbConnection Members

        /// <summary>
        /// Changes the current database for an open Connection object.
        /// </summary>
        /// <param name="asDatabaseName">The name of the database to use in place of the current
        /// database.</param>
        public void ChangeDatabase(string asDatabaseName)
        {
            this.ioConnection.ChangeDatabase(asDatabaseName);
        }

        /// <summary>
        /// Gets the name of the current database or the database to be used after a connection
        /// is opened.
        /// </summary>
        public string Database
        {
            get { return this.ioConnection.Database; }
        }

        /// <summary>
        /// Gets the time to wait while trying to establish a connection before terminating
        /// the attempt and generating an error.
        /// </summary>
        public int ConnectionTimeout
        {
            get { return this.ioConnection.ConnectionTimeout; }
        }

        /// <summary>
        /// Begins a database transaction with the specified IsolationLevel value.
        /// </summary>
        /// <param name="aeLevel">One of the IsolationLevel values.</param>
        /// <returns>An object representing the new transaction.</returns>
        IDbTransaction IDbConnection.BeginTransaction(IsolationLevel aeLevel)
        {
            this.BeginTransaction(aeLevel);
            return this.DbTransaction;
        }

        /// <summary>
        /// Begins a database transaction.
        /// </summary>
        /// <returns>An object representing the new transaction.</returns>
        IDbTransaction IDbConnection.BeginTransaction()
        {
            this.BeginTransaction();
            return this.DbTransaction;
        }

        /// <summary>
        /// Gets or sets the string used to open a database.
        /// </summary>
        public string ConnectionString
        {
            get { return this.ioProperties.ToString(this.ioDBMS.Separator); }
            set
            {
                if (this.IsActive)
                    throw new Exception("teste");
                this.ioProperties.SetString(value, this.ioDBMS.Separator);
            }
        }

        /// <summary>
        /// Creates and returns a Command object associated with the connection.
        /// </summary>
        /// <returns>A Command object associated with the connection.</returns>
        IDbCommand IDbConnection.CreateCommand()
        {
            return this.ioConnection.CreateCommand();
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Finaliza o objeto.
        /// </summary>
        public virtual void Dispose()
        {
            this.ioConnection.Close();
            this.ioConnection.Dispose();
            this.ioProperties.Clear();
            this.ioProperties = null;
            this.ioConnection = null;
        }

        #endregion

        #region DataReader Class

        /// <summary>
        /// Esta classe foi implementada para resolver o problema dos métodos que 
        /// precisam retornar um IDataReader (OpenSql, etc.). Ela encapsula o 
        /// objeto IDbCommand correspondente ao SELECT executado, descartando-o 
        /// depois que o IDataReader é fechado.
        /// Isso resolve o problema do estado da conexão ficar travado em Fetching,
        /// que acontece quando um objeto IDbCommand é destruído antes do fechamento
        /// do seu IDataReader.
        /// </summary>
        protected class DataReader : IDataReader
        {
            private IDbCommand ioCommand;
            private IDataReader ioReader;

            /// <summary>
            /// Construtor.
            /// </summary>
            /// <param name="aoCommand">Objeto IDbCommand.</param>
            public DataReader(IDbCommand aoCommand)
            {
                this.ioCommand = aoCommand;
                this.ioReader = this.ioCommand.ExecuteReader();
            }

#pragma warning disable 1591

            #region IDataReader Members

            /// <summary></summary>
            public int RecordsAffected
            {
                get { return this.ioReader.RecordsAffected; }
            }

            /// <summary></summary>
            public bool IsClosed
            {
                get { return this.ioReader == null; }
            }

            /// <summary></summary><returns></returns>
            public bool NextResult()
            {
                return this.ioReader.NextResult();
            }

            /// <summary></summary>
            public void Close()
            {
                this.ioReader.Close();
                this.Dispose();
            }

            /// <summary></summary><returns></returns>
            public bool Read()
            {
                return this.ioReader.Read();
            }

            /// <summary></summary>
            public int Depth
            {
                get { return this.ioReader.Depth; }
            }

            /// <summary></summary><returns></returns>
            public DataTable GetSchemaTable()
            {
                return this.ioReader.GetSchemaTable();
            }

            #endregion

            #region IDisposable Members

            /// <summary>
            /// Finaliza o objeto.
            /// </summary>
            public void Dispose()
            {
                this.ioReader.Dispose();
                this.ioCommand.Dispose();
                this.ioReader = null;
                this.ioCommand = null;
            }

            #endregion

            #region IDataRecord Members

            public int GetInt32(int i)
            {
                return this.ioReader.GetInt32(i);
            }

            public object this[string name]
            {
                get { return this.ioReader[name]; }
            }

            object IDataRecord.this[int i]
            {
                get { return this.ioReader[i]; }
            }

            public object GetValue(int i)
            {
                return this.ioReader.GetValue(i);
            }

            public bool IsDBNull(int i)
            {
                return this.ioReader.IsDBNull(i);
            }

            public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
            {
                return this.ioReader.GetBytes(i, fieldOffset, buffer, bufferoffset, length);
            }

            public byte GetByte(int i)
            {
                return this.ioReader.GetByte(i);
            }

            public Type GetFieldType(int i)
            {
                return this.ioReader.GetFieldType(i);
            }

            public decimal GetDecimal(int i)
            {
                return this.ioReader.GetDecimal(i);
            }

            public int GetValues(object[] values)
            {
                return this.ioReader.GetValues(values);
            }

            public string GetName(int i)
            {
                return this.ioReader.GetName(i);
            }

            public int FieldCount
            {
                get { return this.ioReader.FieldCount; }
            }

            public long GetInt64(int i)
            {
                return this.ioReader.GetInt64(i);
            }

            public double GetDouble(int i)
            {
                return this.ioReader.GetDouble(i);
            }

            public bool GetBoolean(int i)
            {
                return this.ioReader.GetBoolean(i);
            }

            public Guid GetGuid(int i)
            {
                return this.ioReader.GetGuid(i);
            }

            public DateTime GetDateTime(int i)
            {
                return this.ioReader.GetDateTime(i);
            }

            public int GetOrdinal(string name)
            {
                return this.ioReader.GetOrdinal(name);
            }

            public string GetDataTypeName(int i)
            {
                return this.ioReader.GetDataTypeName(i);
            }

            public float GetFloat(int i)
            {
                return this.ioReader.GetFloat(i);
            }

            public IDataReader GetData(int i)
            {
                return this.ioReader.GetData(i);
            }

            public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
            {
                return this.ioReader.GetChars(i, fieldoffset, buffer, bufferoffset, length);
            }

            public string GetString(int i)
            {
                return this.ioReader.GetString(i);
            }

            public char GetChar(int i)
            {
                return this.ioReader.GetChar(i);
            }

            public short GetInt16(int i)
            {
                return this.ioReader.GetInt16(i);
            }

            #endregion

#pragma warning restore 1591

        } // DataReader

        #endregion

    } // Connection

} // Trends.TFC.Data
